package jrandom.wizards;


import jrandom.JRandomPlugin;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;


public class JRandomTestCaseWizardPage1 extends NewTypeWizardPage {

	private Text classText;
	private Button classButton;
	private IType classToTest;

	private Text fileText;

	private boolean isXMLSelected;
	private boolean isClassSelected;
	private boolean isFileNameReady;

	private IStructuredSelection selection;

	public JRandomTestCaseWizardPage1(IStructuredSelection selection) {
		super(true, "wizardPage1");
		setTitle("JRandom Test Case");
		setDescription("This wizard creates a new JRandom Test Case to test the chosen program" + "\n" +
				" by Random Testing or Adaptive Random Testing.");
		this.selection = selection;
	}

	/**
	 * @see IDialogPage#createControl(Composite)
	 */
	public void createControl(Composite parent) {
		initializeDialogUnits(parent);

		Composite composite = new Composite(parent, SWT.NULL);

		GridLayout layout = new GridLayout();
		int nColumns = 4;

		layout.numColumns = nColumns;
		composite.setLayout(layout);

		// source folder
		createContainerControls(composite, nColumns);

		// package
		createPackageControls(composite, nColumns);

		// class
		createClassControls(composite, nColumns);

		// separator line
		createSeparator(composite, nColumns);

		// output set
		createGroupControls(composite, nColumns);

		initialize();
		setControl(composite);
		setFocus();
	}

	private void createGroupControls(Composite composite, int nColumns) {
		Group group = new Group(composite, SWT.NONE);
		group.setText("Output Set");
		GridData gridData = new GridData();
		gridData.horizontalAlignment = GridData.FILL;
		gridData.horizontalSpan = nColumns;
		group.setLayoutData(gridData);
		GridLayout gridLayout = new GridLayout(nColumns, false);
		group.setLayout(gridLayout);

		createXMLorHTMLControls(group, nColumns);
		createFileNameControls(group, nColumns);
	}

	private void createXMLorHTMLControls(Composite composite, int nColumns) {
		Composite selector = new Composite(composite, SWT.NONE);
		GridData gridData = new GridData();
		gridData.horizontalAlignment = GridData.FILL;
		gridData.horizontalSpan = nColumns;
		selector.setLayoutData(gridData);
		GridLayout gridLayout = new GridLayout(nColumns - 1, false);
		selector.setLayout(gridLayout);

		isXMLSelected = true;
		Button htmlButton = new Button(selector, SWT.RADIO);
		htmlButton.setText("HTML");
		htmlButton.setSelection(!isXMLSelected);
		htmlButton.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, false, false, 1, 1));

		Button xmlButton = new Button(selector, SWT.RADIO);
		xmlButton.setText("XML");
		xmlButton.setSelection(isXMLSelected);
		xmlButton.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, false, false, 1, 1));
		xmlButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				isXMLSelected = ((Button) e.widget).getSelection();
				buttonSelectorChanged();
			}
		});

	}

	private void createFileNameControls(Composite composite, int nColumns) {
		Label label = new Label(composite, SWT.NONE);
		label.setText("File Name:");
		fileText = new Text(composite, SWT.BORDER | SWT.SINGLE);
		GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
		gridData.horizontalSpan = nColumns - 2;
		fileText.setLayoutData(gridData);
		fileText.setText("output." + (isXMLSelected ? "xml" : "html"));
		fileText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				fileTextChanged();
			}
		});
	}

	private void createClassControls(Composite composite, int nColumns) {
		Label classLabel = new Label(composite, SWT.LEFT | SWT.WRAP);
		classLabel.setFont(composite.getFont());
		classLabel.setText("Class:"); 
		classLabel.setLayoutData(new GridData());

		classText= new Text(composite, SWT.SINGLE | SWT.BORDER);
		classText.setEnabled(true);
		classText.setFont(composite.getFont());
		classText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				classTextChanged();
			}
		});
		GridData gridData = new GridData();
		gridData.horizontalAlignment = GridData.FILL;
		gridData.grabExcessHorizontalSpace = true;
		gridData.horizontalSpan = nColumns - 2;
		classText.setLayoutData(gridData);

		classButton = new Button(composite, SWT.PUSH);
		classButton.setText("Browse...");
		classButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				classButtonPressed();
			}
		});

		gridData = new GridData();
		gridData.horizontalAlignment = GridData.FILL;
		gridData.grabExcessHorizontalSpace = false;
		gridData.horizontalSpan = 1;		
		classButton.setLayoutData(gridData);

	}

	private void classButtonPressed() {
		IPackageFragment root = getPackageFragment();
		IJavaElement[] classes = null;
		try {
			if (root != null && root.exists()) {
				classes = root.getChildren();
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		if (classes == null) {
			classes = new IJavaElement[0];
		}

		ElementListSelectionDialog dialog= new ElementListSelectionDialog(getShell(), new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT));
		dialog.setIgnoreCase(true);
		dialog.setTitle("Class Selection");
		dialog.setMessage("choose a file:");
		dialog.setEmptyListMessage("There is no any file satisfied.");
		dialog.setElements(classes);
		dialog.setHelpAvailable(false);

		if (dialog.open() == Window.OK) {
			classToTest = getEnclosingType((IJavaElement) dialog.getFirstResult());
			classText.setText(classToTest.getElementName());
		}
	}

	private void initialize() {
		IJavaElement element = getInitialJavaElement(selection);

		// initialize the source folder field 
		initContainerPage(element);

		// initialize the package field
		initTypePage(element);

		// initialize the class field
		initClassPage(element);

		// initialize the file text state
		fileTextChanged();
	}

	private void initClassPage(IJavaElement element) {
		// put default class to test		
		if (element != null) {
			classToTest = getEnclosingType(element);

			if (classToTest != null) {
				classText.setText(classToTest.getElementName());
			}
		}
	}

	private IType getEnclosingType(IJavaElement element) {
		// evaluate the enclosing type
		IType typeInCompUnit= (IType) element.getAncestor(IJavaElement.TYPE);
		if (typeInCompUnit != null) {
			if (typeInCompUnit.getCompilationUnit() != null) {
				return typeInCompUnit;

			}
		} else {
			ICompilationUnit cu = (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT);
			if (cu != null) {
				return cu.findPrimaryType();

			} else {
				if (element instanceof IClassFile) {
					try {
						IClassFile cf = (IClassFile) element;
						if (cf.isStructureKnown())
							return cf.getType();
					} catch(JavaModelException e) {
						e.printStackTrace();
					}
				}					
			}
		}
		return null;
	}

	public void setVisible(boolean visible) {
		if (isClassSelected && isFileNameReady) {
			updateStatus(new Status(IStatus.OK, JRandomPlugin.PLUGIN_ID, ""));
		} else {
			updateStatus(new Status(IStatus.ERROR, JRandomPlugin.PLUGIN_ID, ""));
		}
		super.setVisible(visible);
	}

	protected IStatus packageChanged() {
		IStatus status = super.packageChanged();

		if (getPackageFragmentRoot() != null && getPackageFragment() != null && getPackageFragment().exists()) {
			classButton.setEnabled(true);
		} else {
			classButton.setEnabled(false);
		}

		classTextChanged();

		if (getPackageFragmentRoot() == null) {
			updatePageStatus("The source folder must exist");
			return status;
		}
		if (getPackageFragment() == null || !getPackageFragment().exists()) {
			updatePageStatus("The package must exist");
			return status;
		}
		return status;
	}

	private void buttonSelectorChanged() {
		if (isXMLSelected) {
			int dotLoc = getFileName().lastIndexOf('.');
			if (dotLoc != -1) {
				String name = getFileName().substring(0, dotLoc + 1);
				setFileName(name + "xml");
			} else {
				setFileName(getFileName() + ".xml");
			}
		} else {
			int dotLoc = getFileName().lastIndexOf('.');
			if (dotLoc != -1) {
				String name = getFileName().substring(0, dotLoc + 1);
				setFileName(name + "html");
			} else {
				setFileName(getFileName() + ".html");
			}
		}
	}

	/**
	 * this function is called when the class text changed.
	 * 
	 */

	private void classTextChanged() {
		IPackageFragment root = getPackageFragment();

		IResource classRes = null;

		isClassSelected = false;

		if (root != null) {
			classRes = ResourcesPlugin.getWorkspace().getRoot()
			.findMember(root.getPath().append(getClassName()+".java"));
		}

		if (getClassName().length() == 0) {
			updatePageStatus("The class to test must be specified");
			return;
		}
		if (classRes == null
				|| (classRes.getType() & (IJavaElement.TYPE | IJavaElement.COMPILATION_UNIT)) == 0) {
			updatePageStatus("The class to test must exist");
			return;
		}
		if (!classRes.isAccessible()) {
			updatePageStatus("Project must be writable");
			return;
		}
		isClassSelected = true;
		classToTest = getEnclosingType(JavaCore.create(classRes));
		if (isFileNameReady) {
			updatePageStatus(null);
		} else {
			fileTextChanged();
		}
	}

	/**
	 * this function is called when the file text changed.
	 * 
	 */

	private void fileTextChanged() {
		String fileName = getFileName();
		int dotLoc = fileName.lastIndexOf('.');
		isFileNameReady = false;

		if (fileName.length() == 0 || dotLoc == 0) {
			updatePageStatus("The file name must be specified");
			return;
		}
		if (fileName.replace('\\', '/').indexOf('/', 1) > 0) {
			updatePageStatus("The file name must be valid");
			return;
		}
		
		if (dotLoc != -1) {
			String ext = fileName.substring(dotLoc + 1);
			if (isXMLSelected && ext.equalsIgnoreCase("xml") == false) {
				updatePageStatus("File extension must be \"xml\"");
				return;
			}
			if (!isXMLSelected && ext.equalsIgnoreCase("html") == false) {
				updatePageStatus("File extension must be \"html\"");
				return;
			}
		} else {
			fileText.append(isXMLSelected ? ".xml" : ".html");
			fileText.setSelection(fileName.length());
		}
		
		isFileNameReady = true;
		if (isClassSelected) {
			updatePageStatus(null);
		} else {
			classTextChanged();
		}
	}

	private void updatePageStatus(String message) {
		setErrorMessage(message);
		setPageComplete(message == null);
	}

	public IType getClassToTest() {
		return classToTest;
	}

	public String getClassName() {
		return classText.getText();
	}

	public String getFileName() {
		return fileText.getText();
	}

	public String getSourceFolder() {
		return getContainerLabel();
	}

	public void setFileName(String str) {
		fileText.setText(str);
	}

	public boolean isXMLSelected() {
		return isXMLSelected;
	}
}